<html>
	<head>
		<script language="JavaScript" type="text/javascript" src="../app/jsUnitCore.js"></script>
		<script language="JavaScript" type="text/javascript" src="../../jsmock.js"></script>
		<script language="JavaScript" type="text/javascript" src="../helpers.js"></script>
		<script language="JavaScript" type="text/javascript">

			var control;
			function setUp() {
				control = new MockControl();
			}

			/* HELPERS */
			function TestObject() {}
			TestObject.prototype = {
				helloWorld: function() {},
				sayHello: function() {},
				__goodBye:  function() {},
				billy: "hello"
			}

			TestObject.classFunction = function() {};
			TestObject.club = 12;

			function StaticGuy() {}
			StaticGuy.doThis = function() {};
			StaticGuy.doThat = function() {};
			StaticGuy.__private = function() {};

			var Singleton = {
				foo: function() {},
				bar: function() {},
				__hide: function() {}
			}

			function InstanceObject() {
				this.epictetus = function() {};
				this.plato = function() {};
			}

			/* Tests */
			function test_mock_control_constructor() {
				assertNotNull(control.__expectationMatcher);
				assertNull(control.__lastCallName);
			}

			function test_createMock() {
				var mock = control.createMock(TestObject);			

				try {
					mock.helloWorld();
					mock.sayHello();
					mock.classFunction();
				} catch(e) {
					fail("helloWorld(), sayHello(), and classFunction() should exist");
				}

				assertEquals("Control should be the same", control, mock.expect().helloWorld());
				assertEquals("Control should be the same", control, mock.expect().sayHello());
				assertEquals("Control should be the same", control, mock.expect().classFunction());

				assertUndefined(mock.__goodBye);
				assertUndefined(mock.club);
				assertUndefined(mock.billy);
				
			}

			function test_createMock_class_functions_mocked()
			{
				var mock = control.createMock(StaticGuy);
				
				try {
					mock.doThis();
					mock.doThat();
				}
				catch(e) {
					fail("dothis(), and doThat() should exist");
				}

				assertUndefined(mock.__private);

			}

			function test_createMock_singleton_passed_in() {
				var mock = control.createMock(Singleton);	
				
				try {
					mock.foo();
					mock.bar();
				} catch(e) {
					fail("foo() and bar() should exist");
				}

				assertEquals("Control should be the same", control, mock.expect().foo());
				assertEquals("Control should be the same", control, mock.expect().bar());
				assertUndefined(mock.__hide);
				
			}

			function test_createMock_instance_object_passed_in() {
				var mock = null;
				try {
					mock = control.createMock(new InstanceObject());
				} catch(e) {
					fail("Should not have thrown exception");
				}

				assertNotUndefined("Should not be undefined", mock);

				try {
					mock.epictetus();
					mock.plato();
				} catch(e) {
					fail("epictetus() and plato() should exist");
				}

				assertEquals("Control should be the same", control, mock.expect().epictetus());
				assertEquals("Control should be the same", control, mock.expect().plato());
			}

			function test_createMock_object_has_no_properties() {
				try {
					var mock = control.createMock(Array);
				} catch(e) {
					assertEquals("Object does not have any accessible functions.", e.message);
					return;
				}
				fail("Should have thrown an exception");
			}

			function test_createMock_primitive_passed_in() {
				try {
					var mock = control.createMock(1);
				} catch(e) {
					assertEquals("Cannot mock out a " + typeof(1), e.message);
					return;
				}

				fail("Should have thrown exception");
			}

			function test_andReturn() {
				var mock = control.createMock(TestObject);

				mock.expect().helloWorld();		
				control.andReturn("stewie");
				mock.expect().sayHello();
				control.andReturn("billy");
				mock.expect().helloWorld().andReturn("Foo");
				mock.expect().sayHello().andReturn("Bar");

				assertEquals("stewie", mock.helloWorld());
				assertEquals("billy", mock.sayHello());
				assertEquals("Foo", mock.helloWorld());
				assertEquals("Bar", mock.sayHello());

				assertUndefined(mock.helloWorld());
				assertUndefined(mock.sayHello());

				mock.expect().sayHello().andReturn("Bar");
				assertEquals("Bar", mock.sayHello());

			}

			function test_andReturn_no_expectation_made() {
				try {
					control.andReturn(1);
				} catch(e) {
					assertEquals("Cannot set return value without an expectation", e.message);
					return;
				}

				fail("Should have thrown exception");
			}

			function test_andThrow() {
				var mock = control.createMock(TestObject);
				
				mock.expect().helloWorld();
				control.andThrow("Big big problem");
				mock.expect().sayHello();
				control.andThrow("What's up doc?");

				assertThrows(function() { mock.helloWorld() }, "Big big problem");
				assertThrows(function() { mock.sayHello() }, "What's up doc?");

				assertUndefined(mock.sayHello());
				assertUndefined(mock.helloWorld());

				mock.expect().helloWorld().andThrow("Homer");
				assertThrows(function() { mock.helloWorld() }, "Homer");
			}

			function test_andThrow_no_expectation_made() {
				assertThrows(function() { control.andThrow("boo") }, "Cannot throw error without an expectation");
			}

			function test_andStub() {
				var mock = control.createMock(TestObject);
				var fred = 0;
				var testObject = new TestObject();

				mock.expect().helloWorld();
				control.andStub(function() {fred++; });
				mock.expect().sayHello();	
				control.andStub(function() {fred--;});

				mock.helloWorld();
				assertEquals(1, fred);
				mock.sayHello();
				assertEquals(0, fred);

				assertUndefined(mock.helloWorld());
				assertUndefined(mock.sayHello());

				mock.expect().helloWorld().andStub( function() {fred = 100; });
				mock.helloWorld();
				assertEquals(100, fred);


				
			}

			function test_andStub_passing_arguments() {
				var mock = control.createMock(TestObject);
				var testObject = new TestObject();

				/* One Arguments */
				mock.expect().helloWorld().andStub(function() { assertEquals( "Bart", arguments[0]); });
				mock.helloWorld("Bart");

				/* Multiple Arguments */
				mock.expect().sayHello().andStub(function() { assertEquals(10, arguments.length);
																											for(i = 0; i < arguments.length; i++) { 
																												assertEquals(i, arguments[i]); } });
				mock.sayHello(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

				/* Complex types, e.g. objects */

				mock.expect().sayHello().andStub(function() { assertEquals(testObject, arguments[0]); });
				mock.sayHello(testObject);
			}

			function test_andStub_no_expectation_made() {
				assertThrows(function() { control.andStub(function() {}) }, "Cannot stub without an expectation");
			}

			function test_andStub_no_function_passed() {
				var mock = control.createMock(TestObject);
				assertThrows(function() { mock.expect().helloWorld().andStub(1) }, "Stub must be a function");
			}

			function test_verify()
			{
				var mock = control.createMock(TestObject);

				mock.expect().helloWorld("hello", 1, 2);
				mock.expect().sayHello();
				mock.expect().helloWorld();
				mock.expect().sayHello("bam");

				mock.helloWorld("hello", 1, 2);
				mock.sayHello();
				mock.helloWorld(); 
				mock.sayHello("bam");

				try {
					control.verify();
				}
				catch(e) {
					fail("Verify should have passed");
				}

				mock.expect().helloWorld("hello", 1, 2);
				mock.expect().sayHello();

				mock.helloWorld("hello", 1, 2);
				mock.sayHello();

				try {
					control.verify();
				} 
				catch(e) {
					fail("Verify should have passed");
				}
			}

			function test_verify_fails()
			{
				var mock = control.createMock(TestObject);

				mock.expect().helloWorld("sam");
				mock.expect().sayHello();

				mock.helloWorld("adams");
				mock.sayHello();

				assertThrows(function() { control.verify() }, "Unexpected Arguments: helloWorld(\"adams\")");

				mock.expect().helloWorld(1,2);
				mock.helloWorld(1, 2, ["hello", "world"]);

				assertThrows(function() { control.verify() }, "Unexpected Arguments: helloWorld(1, 2, [\"hello\", \"world\"])");

				mock.expect().helloWorld();
				mock.sayHello();

				assertThrows( function() { control.verify() }, "Surprise call: sayHello()");
			}
		</script>
	</head>
	<body>
		<p>MockControlTest</p>
	</body>
</html>
